package com.gobang.service.impl;

import com.gobang.domain.enums.GameStatus;
import com.gobang.domain.enums.PlayerColor;
import com.gobang.service.BoardManager;
import com.gobang.service.GameEngine;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 游戏引擎实现类
 * 使用状态机模式管理游戏流程和状态转换
 * 
 * @author System
 * @since 1.0.0
 */
@Service
@RequiredArgsConstructor
public class GameEngineImpl implements GameEngine {

    private static final Logger log = LoggerFactory.getLogger(GameEngineImpl.class);

    private final StringRedisTemplate redisTemplate;
    private final BoardManager boardManager;

    /**
     * 状态转换映射表
     * 定义合法的状态转换
     */
    private static final Map<GameStatus, Set<GameStatus>> STATE_TRANSITIONS = new HashMap<>();

    // 状态机维护：只能从某一状态转换为其他特定状态
    // 1. 前置为[等待状态]，则后置只能是[游戏中状态]/[取消状态]
    // 2. 前置为[游戏中状态]，则后置只能是[已完成状态]/[取消状态]
    // 3. 终态：[已完成状态]、[取消状态]
    static {
        STATE_TRANSITIONS.put(GameStatus.WAITING, Set.of(GameStatus.PLAYING, GameStatus.CANCELLED));
        STATE_TRANSITIONS.put(GameStatus.PLAYING, Set.of(GameStatus.FINISHED, GameStatus.CANCELLED));
        STATE_TRANSITIONS.put(GameStatus.FINISHED, Set.of());
        STATE_TRANSITIONS.put(GameStatus.CANCELLED, Set.of());
    }

    @Override
    public boolean initializeGame(String roomId, Long blackPlayerId, Long whitePlayerId) {
        log.info("初始化游戏房间: {}, 黑方: {}, 白方: {}", roomId, blackPlayerId, whitePlayerId);
        
        try {
            String roomKey = "room:" + roomId;
            
            // 设置房间基础信息
            redisTemplate.opsForHash().put(roomKey, "black_player_id", blackPlayerId.toString());
            redisTemplate.opsForHash().put(roomKey, "white_player_id", whitePlayerId.toString());
            redisTemplate.opsForHash().put(roomKey, "current_turn", PlayerColor.BLACK.getCode());
            redisTemplate.opsForHash().put(roomKey, "game_status", GameStatus.WAITING.getCode());
            redisTemplate.opsForHash().put(roomKey, "move_count", "0");
            redisTemplate.opsForHash().put(roomKey, "created_at", String.valueOf(System.currentTimeMillis()));
            
            // 初始化棋盘状态（15x15空棋盘）
            int[][] emptyBoard = new int[15][15];
            String boardJson = boardManager.getBoardJson(emptyBoard);
            // String boardJson = convertBoardToJson(emptyBoard);
            redisTemplate.opsForHash().put(roomKey, "board_state", boardJson);
            
            log.info("游戏房间初始化成功: {}", roomId);
            return true;
            
        } catch (Exception e) {
            log.error("初始化游戏房间失败: {}", roomId, e);
            return false;
        }
    }

    @Override
    public boolean startGame(String roomId) {
        log.info("开始游戏: {}", roomId);
        
        GameStatus currentStatus = getGameStatus(roomId);
        if (currentStatus != GameStatus.WAITING) {
            log.warn("游戏状态不允许开始，当前状态: {}, 房间: {}", currentStatus, roomId);
            return false;
        }
        
        return transitionGameState(roomId, GameStatus.WAITING, GameStatus.PLAYING);
    }

    @Override
    public boolean transitionGameState(String roomId, GameStatus fromStatus, GameStatus toStatus) {
        log.debug("状态转换: {} -> {}, 房间: {}", fromStatus, toStatus, roomId);
        
        if (!isValidStateTransition(fromStatus, toStatus)) {
            log.warn("非法状态转换: {} -> {}, 房间: {}", fromStatus, toStatus, roomId);
            return false;
        }
        
        try {
            String roomKey = "room:" + roomId;
            redisTemplate.opsForHash().put(roomKey, "game_status", toStatus.getCode());
            redisTemplate.opsForHash().put(roomKey, "last_update_time", String.valueOf(System.currentTimeMillis()));
            
            log.info("状态转换成功: {} -> {}, 房间: {}", fromStatus, toStatus, roomId);
            return true;
            
        } catch (Exception e) {
            log.error("状态转换失败: {} -> {}, 房间: {}", fromStatus, toStatus, roomId, e);
            return false;
        }
    }

    @Override
    public PlayerColor switchTurn(String roomId) {
        log.debug("切换回合: {}", roomId);
        
        PlayerColor currentTurn = getCurrentTurn(roomId);
        if (currentTurn == null) {
            log.warn("无法获取当前回合信息: {}", roomId);
            return null;
        }
        
        PlayerColor nextTurn = currentTurn.getOpponent();
        
        try {
            String roomKey = "room:" + roomId;
            redisTemplate.opsForHash().put(roomKey, "current_turn", nextTurn.getCode());
            
            log.debug("回合切换成功: {} -> {}, 房间: {}", currentTurn, nextTurn, roomId);
            return nextTurn;
            
        } catch (Exception e) {
            log.error("切换回合失败: {}", roomId, e);
            return null;
        }
    }

    @Override
    public PlayerColor getCurrentTurn(String roomId) {
        try {
            String roomKey = "room:" + roomId;
            String currentTurnCode = (String) redisTemplate.opsForHash().get(roomKey, "current_turn");
            
            if (currentTurnCode == null) {
                log.warn("房间不存在或无当前回合信息: {}", roomId);
                return null;
            }
            
            return PlayerColor.fromCode(currentTurnCode);
            
        } catch (Exception e) {
            log.error("获取当前回合失败: {}", roomId, e);
            return null;
        }
    }

    @Override
    public boolean isPlayerTurn(String roomId, Long userId) {
        PlayerColor currentTurn = getCurrentTurn(roomId);
        PlayerColor playerColor = getPlayerColor(roomId, userId);
        
        if (currentTurn == null || playerColor == null) {
            return false;
        }
        
        return currentTurn == playerColor;
    }

    @Override
    public PlayerColor getPlayerColor(String roomId, Long userId) {
        try {
            String roomKey = "room:" + roomId;
            String blackPlayerId = (String) redisTemplate.opsForHash().get(roomKey, "black_player_id");
            String whitePlayerId = (String) redisTemplate.opsForHash().get(roomKey, "white_player_id");
            
            if (userId.toString().equals(blackPlayerId)) {
                return PlayerColor.BLACK;
            } else if (userId.toString().equals(whitePlayerId)) {
                return PlayerColor.WHITE;
            } else {
                log.warn("用户不在房间中: userId={}, roomId={}", userId, roomId);
                return null;
            }
            
        } catch (Exception e) {
            log.error("获取玩家颜色失败: userId={}, roomId={}", userId, roomId, e);
            return null;
        }
    }

    @Override
    public boolean endGame(String roomId, Long winnerId, String reason) {
        log.info("结束游戏: roomId={}, winnerId={}, reason={}", roomId, winnerId, reason);
        
        GameStatus currentStatus = getGameStatus(roomId);
        if (currentStatus != GameStatus.PLAYING) {
            log.warn("游戏状态不允许结束，当前状态: {}, 房间: {}", currentStatus, roomId);
            return false;
        }
        
        try {
            String roomKey = "room:" + roomId;
            redisTemplate.opsForHash().put(roomKey, "winner_id", winnerId.toString());
            redisTemplate.opsForHash().put(roomKey, "end_reason", reason);
            
            return transitionGameState(roomId, currentStatus, GameStatus.FINISHED);
            
        } catch (Exception e) {
            log.error("结束游戏失败: {}", roomId, e);
            return false;
        }
    }

    @Override
    public boolean cancelGame(String roomId, String reason) {
        log.info("取消游戏: roomId={}, reason={}", roomId, reason);
        
        GameStatus currentStatus = getGameStatus(roomId);
        
        try {
            String roomKey = "room:" + roomId;
            redisTemplate.opsForHash().put(roomKey, "cancel_reason", reason);
            
            return transitionGameState(roomId, currentStatus, GameStatus.CANCELLED);
            
        } catch (Exception e) {
            log.error("取消游戏失败: {}", roomId, e);
            return false;
        }
    }

    @Override
    public GameStatus getGameStatus(String roomId) {
        try {
            String roomKey = "room:" + roomId;
            String statusCode = (String) redisTemplate.opsForHash().get(roomKey, "game_status");
            
            if (statusCode == null) {
                log.warn("房间不存在: {}", roomId);
                return null;
            }
            
            return GameStatus.fromCode(statusCode);
            
        } catch (Exception e) {
            log.error("获取游戏状态失败: {}", roomId, e);
            return null;
        }
    }

    @Override
    public boolean isValidStateTransition(GameStatus fromStatus, GameStatus toStatus) {
        if (fromStatus == null || toStatus == null) {
            return false;
        }
        
        Set<GameStatus> validTransitions = STATE_TRANSITIONS.get(fromStatus);
        return validTransitions != null && validTransitions.contains(toStatus);
    }

    /**
     * 将棋盘状态转换为JSON字符串
     *
     * @param board 棋盘状态
     * @return JSON字符串     [[0,0,0],[0,0,0],[0,0,0]]
     */
    private String convertBoardToJson(int[][] board) {
        StringBuilder sb = new StringBuilder();
        // {{0,0,0},{0,0,0},{0,0,0}}
        // i = 3; j = 3
        // 缓存格式 [[0,0,0],[0,0,0],[0,0,0]]
        sb.append("[");
        for (int i = 0; i < board.length; i++) {
            if (i > 0) sb.append(",");
            sb.append("[");
            for (int j = 0; j < board[i].length; j++) {
                if (j > 0) sb.append(",");
                sb.append(board[i][j]);
            }
            sb.append("]");
        }
        sb.append("]");
        return sb.toString();
    }
} 